Skip to content

Identify which CLI an upgrade warning is about - #2670

Open
Dione-b wants to merge 2 commits into
stellar:mainfrom
Nearx-Labs:develop
Open

Identify which CLI an upgrade warning is about#2670
Dione-b wants to merge 2 commits into
stellar:mainfrom
Nearx-Labs:develop

Conversation

@Dione-b

@Dione-b Dione-b commented Aug 4, 2026

Copy link
Copy Markdown

What problem does your feature solve?

The upgrade warning names a version but not the install it came from, and on a machine with more than one Stellar CLI those are different questions.

A stale soroban left in ~/.cargo/bin by an old cargo install, or a Homebrew install shadowed by a newer one, prints A new release of Stellar CLI is available: 22.1.0 -> 23.3.0 using its own version. The user compares that against stellar --version, which answers 25.2.0. The numbers disagree because two different binaries are talking — and the message gives no way to see that.

The reported latest version could also be stale on its own. The check runs in a background task that main drops on return, so a command finishing faster than the request to crates.io left the fetched versions unwritten and the next run starting over — re-fetching every time while continuing to report whatever the cache already held.

What would you like to see?

  • The upgrade warning names the executable it is about, so a warning from an old binary is self-identifying.
  • The background check gets a grace period to write its result before the process exits, so a fast command no longer discards the versions it just fetched.
  • The crates.io fetch gets a full-request timeout (5s). The shared HTTP client only bounds connect time, so a server that accepts and then stalls could hang the request indefinitely — and no grace period can be chosen against a wait with no ceiling. The grace is that timeout plus a second, so it cannot expire before a fetch its own timeout allowed to succeed.

What alternatives are there?

Accepting the known limitations:

  • This cannot fix warnings printed by already-released binaries. An old soroban will keep printing its old unannotated message — the fix has to live in the binary doing the printing. What it fixes is every warning from this release forward.
  • The grace period is skipped on error paths that call process::exit.
  • It may add up to 6 seconds to the first command of the day, and only when the check actually goes to the network. The previous behaviour was to drop the check entirely. STELLAR_NO_UPDATE_CHECK still disables it.

Diagnostics, split out

An earlier revision of this PR also taught stellar doctor to list every stellar/soroban executable on PATH with its version, and to report which install last wrote the shared version cache. That is diagnosis rather than fix — useful for the case above that no code change can reach — and it was ~90% of the diff. It has been split out so this PR stays reviewable, and will follow separately.

fix: #2464

Copilot AI balanced review requested due to automatic review settings August 4, 2026 19:45
@github-project-automation github-project-automation Bot moved this to Backlog (Not Ready) in DevX Aug 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

Doctor can report false installation mismatches, omit single-install details, and hang on unresponsive executables.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

Improves upgrade diagnostics for systems with multiple Stellar CLI installations.

Changes:

  • Identifies the executable responsible for upgrade warnings and cache writes.
  • Expands doctor diagnostics to inspect PATH installations.
  • Adds request and shutdown timeouts for background upgrade checks.
File summaries
File Description
cmd/soroban-cli/src/upgrade_check.rs Adds executable identification, cache attribution, and fetch timeout.
cmd/soroban-cli/src/config/upgrade_check.rs Persists the cache writer with backward compatibility.
cmd/soroban-cli/src/commands/doctor.rs Reports installations and cache-writer mismatches.
cmd/soroban-cli/src/cli.rs Gives background checks a completion grace period.
Review details

Suppressed comments (1)

cmd/soroban-cli/src/commands/doctor.rs:242

  • The usual single-install case returns before printing the executable's path and version, so doctor does not actually list every PATH installation as promised. The zero-match case is also reported as “Only one.” Always enumerate nonempty results and handle zero separately.
    if installs.len() <= 1 {
        print.checkln("Only one Stellar CLI found on PATH".to_string());
        return;
  • Files reviewed: 4/4 changed files
  • Comments generated: 3
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread cmd/soroban-cli/src/commands/doctor.rs Outdated
Comment thread cmd/soroban-cli/src/commands/doctor.rs Outdated
Comment thread cmd/soroban-cli/src/commands/doctor.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Some installation listings and cache-writer diagnostics are incomplete or misleading.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (3)

cmd/soroban-cli/src/upgrade_check.rs:140

  • This assigns last_checked_by after a failed request without refreshing either cached version, but doctor later tells users the cache was “last refreshed by” this executable. That can falsely attribute version data to an install that never fetched it. Keep this assignment if the intended identity is the last writer/pacer, but update the field documentation and all doctor messages to say “last checked/written by”; alternatively, record this field only after successful refreshes.
                // A failed attempt still paces the next one, so record who
                // paced it -- otherwise the file credits whichever install
                // last succeeded, which may not be the one holding it back.
                stats.last_checked_by = Some(check_performed_by());

cmd/soroban-cli/src/commands/doctor.rs:264

  • The single-install branch does not call list_installs, so doctor omits that executable's PATH location and version. This contradicts the PR's stated behavior of listing every discovered stellar/soroban executable with its version; the separate “Running executable” line is not necessarily the PATH entry and has no version.
        (1, _) => print.checkln("Only one Stellar CLI found on PATH".to_string()),

cmd/soroban-cli/src/commands/doctor.rs:277

  • common_version == None also means one or more probes returned None, not necessarily that known versions differ. For example, two executables that cannot run are both listed as “unknown version” while this branch claims they reported different versions. Distinguish probe failures from genuinely distinct known versions so the diagnostic does not give a false cause.
        (count, None) => {
            print.warnln(format!(
                "Found {count} Stellar CLI executables on PATH reporting different versions; \
                 an outdated one can report a version that disagrees with `stellar --version`:"
            ));
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Dione-b added a commit to Nearx-Labs/stellar-cli that referenced this pull request Aug 6, 2026
Motivation: Copilot's review of stellar#2670 found three
`doctor` diagnostics claiming more than the data behind them supports,
each able to send a user after a cause that was never observed.

Behavior:

- A check whose fetch failed still stamps the cache, but leaves the
  recorded versions untouched, so "last refreshed by" credited an
  install with version data it never fetched. Say "checked" instead, in
  the messages and the field docs: the writer paces the next check
  rather than vouching for the versions stored beside it. Recording it
  only after a successful fetch was the alternative, and it hides the
  install worth finding -- a stale one whose fetch fails still
  suppresses everyone else's check for a day.
- The single-install branch printed a count without the listing, so the
  one case a listing would settle was the one case that omitted path and
  version. List every discovered install.
- Absent agreement was reported as disagreement: two executables that
  cannot be run are both unknown, yet the message blamed differing
  versions. `InstalledVersions` now keeps Agreed, Disagree and
  Unanswered apart. An observed disagreement still wins over a failed
  probe alongside it, because that one is a fact.

Tests: four unit cases for `summarize_versions`, and two integration
cases driving real subprocesses through a fake `PATH` -- two unrunnable
CLIs, and a disagreement sitting next to an unreadable binary. The
lone-install listing and the reworded cache-writer lines are asserted
too.

Release impact: no breaking change and no migration. The
`upgrade_check.json` shape is untouched and older files still load. Only
`doctor`'s stderr wording changes -- no command, flag or help text does,
so `FULL_HELP_DOCS.md` stands as is.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
Dione-b added a commit to Nearx-Labs/stellar-cli that referenced this pull request Aug 6, 2026
Motivation: review of stellar#2670 found the taxonomy added in
b7df621 over-claiming in two places of its own -- the same class of error
it exists to remove, one size smaller.

Behavior:

- `Disagree` counted every executable found, including ones that never
  answered. Two reporting different versions beside a third that could not
  be run printed "Found 3 Stellar CLI executables on PATH reporting
  different versions", contradicted by the listing directly beneath it,
  where the third reads "(unknown version)". It now carries how many went
  unanswered and names only the ones that were heard from: "the 2 that
  reported a version do not agree (1 could not be asked)".
- `Unanswered` discarded what the answering executables established. Two at
  27.1.0 beside one that cannot run is a machine whose reachable installs
  agree, and that was the most useful fact on the line; the message said
  only that agreement could not be determined. It now carries that version
  and leads with it, while still declining to call the whole set agreed:
  a version that was never read cannot be ruled out.

Every count now sits next to what it counts, so the sentence can be checked
against the listing below it.

Also drops the redundant `return` in the zero-install arm. `list_installs`
prints nothing for an empty slice, so the early exit bought nothing and
only broke the symmetry between arms.

Tests: unit cases pin the new payloads, including that an agreement
survives a failed probe beside it and that the disagreement count excludes
the executable that never answered. An integration case drives the
agreement-plus-unreadable scenario through real subprocesses, and the two
existing messages that changed are re-pinned.

Release impact: no breaking change and no migration -- only `doctor`'s
stderr wording moves, so `FULL_HELP_DOCS.md` stands as is.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
Dione-b added a commit to Nearx-Labs/stellar-cli that referenced this pull request Aug 7, 2026
Motivation: Copilot's review of stellar#2670 found three
`doctor` diagnostics claiming more than the data behind them supports,
each able to send a user after a cause that was never observed.

Behavior:

- A check whose fetch failed still stamps the cache, but leaves the
  recorded versions untouched, so "last refreshed by" credited an
  install with version data it never fetched. Say "checked" instead, in
  the messages and the field docs: the writer paces the next check
  rather than vouching for the versions stored beside it. Recording it
  only after a successful fetch was the alternative, and it hides the
  install worth finding -- a stale one whose fetch fails still
  suppresses everyone else's check for a day.
- The single-install branch printed a count without the listing, so the
  one case a listing would settle was the one case that omitted path and
  version. List every discovered install.
- Absent agreement was reported as disagreement: two executables that
  cannot be run are both unknown, yet the message blamed differing
  versions. `InstalledVersions` now keeps Agreed, Disagree and
  Unanswered apart. An observed disagreement still wins over a failed
  probe alongside it, because that one is a fact.

Tests: four unit cases for `summarize_versions`, and two integration
cases driving real subprocesses through a fake `PATH` -- two unrunnable
CLIs, and a disagreement sitting next to an unreadable binary. The
lone-install listing and the reworded cache-writer lines are asserted
too.

Release impact: no breaking change and no migration. The
`upgrade_check.json` shape is untouched and older files still load. Only
`doctor`'s stderr wording changes -- no command, flag or help text does,
so `FULL_HELP_DOCS.md` stands as is.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
Dione-b added a commit to Nearx-Labs/stellar-cli that referenced this pull request Aug 7, 2026
Motivation: review of stellar#2670 found the taxonomy added in
b7df621 over-claiming in two places of its own -- the same class of error
it exists to remove, one size smaller.

Behavior:

- `Disagree` counted every executable found, including ones that never
  answered. Two reporting different versions beside a third that could not
  be run printed "Found 3 Stellar CLI executables on PATH reporting
  different versions", contradicted by the listing directly beneath it,
  where the third reads "(unknown version)". It now carries how many went
  unanswered and names only the ones that were heard from: "the 2 that
  reported a version do not agree (1 could not be asked)".
- `Unanswered` discarded what the answering executables established. Two at
  27.1.0 beside one that cannot run is a machine whose reachable installs
  agree, and that was the most useful fact on the line; the message said
  only that agreement could not be determined. It now carries that version
  and leads with it, while still declining to call the whole set agreed:
  a version that was never read cannot be ruled out.

Every count now sits next to what it counts, so the sentence can be checked
against the listing below it.

Also drops the redundant `return` in the zero-install arm. `list_installs`
prints nothing for an empty slice, so the early exit bought nothing and
only broke the symmetry between arms.

Tests: unit cases pin the new payloads, including that an agreement
survives a failed probe beside it and that the disagreement count excludes
the executable that never answered. An integration case drives the
agreement-plus-unreadable scenario through real subprocesses, and the two
existing messages that changed are re-pinned.

Release impact: no breaking change and no migration -- only `doctor`'s
stderr wording moves, so `FULL_HELP_DOCS.md` stands as is.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
Dione-b added a commit to Nearx-Labs/stellar-cli that referenced this pull request Aug 7, 2026
Motivation: Copilot's review of stellar#2670 found three
`doctor` diagnostics claiming more than the data behind them supports,
each able to send a user after a cause that was never observed.

Behavior:

- A check whose fetch failed still stamps the cache, but leaves the
  recorded versions untouched, so "last refreshed by" credited an
  install with version data it never fetched. Say "checked" instead, in
  the messages and the field docs: the writer paces the next check
  rather than vouching for the versions stored beside it. Recording it
  only after a successful fetch was the alternative, and it hides the
  install worth finding -- a stale one whose fetch fails still
  suppresses everyone else's check for a day.
- The single-install branch printed a count without the listing, so the
  one case a listing would settle was the one case that omitted path and
  version. List every discovered install.
- Absent agreement was reported as disagreement: two executables that
  cannot be run are both unknown, yet the message blamed differing
  versions. `InstalledVersions` now keeps Agreed, Disagree and
  Unanswered apart. An observed disagreement still wins over a failed
  probe alongside it, because that one is a fact.

Tests: four unit cases for `summarize_versions`, and two integration
cases driving real subprocesses through a fake `PATH` -- two unrunnable
CLIs, and a disagreement sitting next to an unreadable binary. The
lone-install listing and the reworded cache-writer lines are asserted
too.

Release impact: no breaking change and no migration. The
`upgrade_check.json` shape is untouched and older files still load. Only
`doctor`'s stderr wording changes -- no command, flag or help text does,
so `FULL_HELP_DOCS.md` stands as is.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
Dione-b added a commit to Nearx-Labs/stellar-cli that referenced this pull request Aug 7, 2026
Motivation: review of stellar#2670 found the taxonomy added in
b7df621 over-claiming in two places of its own -- the same class of error
it exists to remove, one size smaller.

Behavior:

- `Disagree` counted every executable found, including ones that never
  answered. Two reporting different versions beside a third that could not
  be run printed "Found 3 Stellar CLI executables on PATH reporting
  different versions", contradicted by the listing directly beneath it,
  where the third reads "(unknown version)". It now carries how many went
  unanswered and names only the ones that were heard from: "the 2 that
  reported a version do not agree (1 could not be asked)".
- `Unanswered` discarded what the answering executables established. Two at
  27.1.0 beside one that cannot run is a machine whose reachable installs
  agree, and that was the most useful fact on the line; the message said
  only that agreement could not be determined. It now carries that version
  and leads with it, while still declining to call the whole set agreed:
  a version that was never read cannot be ruled out.

Every count now sits next to what it counts, so the sentence can be checked
against the listing below it.

Also drops the redundant `return` in the zero-install arm. `list_installs`
prints nothing for an empty slice, so the early exit bought nothing and
only broke the symmetry between arms.

Tests: unit cases pin the new payloads, including that an agreement
survives a failed probe beside it and that the disagreement count excludes
the executable that never answered. An integration case drives the
agreement-plus-unreadable scenario through real subprocesses, and the two
existing messages that changed are re-pinned.

Release impact: no breaking change and no migration -- only `doctor`'s
stderr wording moves, so `FULL_HELP_DOCS.md` stands as is.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
@Dione-b
Dione-b requested a balanced review from Copilot August 7, 2026 23:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (3)

cmd/soroban-cli/src/commands/doctor.rs:266

  • A single discovered executable is always shown with a success marker, even when its version probe failed and the list immediately labels it unknown version. This makes a broken or non-Stellar executable look healthy, whereas the same failed probe correctly warns when multiple entries exist. Match InstalledVersions::Agreed for the success case and warn for Unanswered.
        // One file, so nothing can disagree with it. Still list it: the running
        // executable is not necessarily the one `PATH` resolves by name, and the
        // line above carries no version.
        (1, _) => print.checkln("Only one Stellar CLI found on PATH:".to_string()),

cmd/soroban-cli/src/upgrade_check.rs:48

  • Using the canonical target as the installation identity is not stable across symlink-managed upgrades. For example, Homebrew keeps /opt/homebrew/bin/stellar as the user-facing install path but retargets it from one versioned Cellar path to another; this cache value therefore changes after a normal in-place upgrade and doctor reports a different CLI. Preserve or separately record a stable invoked/install path for identity, while retaining the resolved path only as diagnostic detail.
    let path = std::env::current_exe().ok()?;
    let path = path.canonicalize().unwrap_or(path);

cmd/soroban-cli/src/commands/doctor.rs:54

  • This read can race with the ordinary upgrade-check task spawned in cli.rs:81 before root.run(). If the cache is stale and that task completes or fails quickly, it writes this executable as last_checked_by before this load, hiding the different-install warning that this snapshot is meant to preserve. Skip the global background check for doctor, or capture/pass the previous writer before spawning it.

This issue also appears on line 263 of the same file.

        // Read this before `check_version`, which refreshes the cache and would
        // otherwise record this very run as the writer -- hiding the mismatch
        // the report exists to reveal.
        let previous_cache_writer = version_cache_writer();

@Dione-b

Dione-b commented Aug 8, 2026

Copy link
Copy Markdown
Author

Addressed the three findings from the latest review, one commit each.

A lone install is shown with a success marker even when its probe failed39bc91c

The arm matched on the count alone, so a single install got a success tick above a line reading unknown version, while the identical evidence warns as soon as a second executable exists. Now split on what the probe established: Agreed keeps the tick, Unanswered warns. A disagreement needs two known versions, so one executable cannot produce one — that case is left to the count-based arms rather than given a message here that would misdescribe it, and InstalledVersions::Disagree records why it cannot arise. Covered by warns_when_the_only_install_cannot_report_a_version.

The canonical path is not stable across symlink-managed upgradesdec74ce

Agreed, and it is the same false mismatch already taken out of the version comparison, arriving by way of the path instead. running_binary no longer canonicalizes: the path the user invokes outlives the file it happens to point at, which is what makes it identity rather than a snapshot, and it is also the path they can act on when a warning names it.

Two consequences worth flagging, since neither was in the finding:

  • Reporting had to follow identity. Leaving the running executable unresolved while find_installs stayed canonical would print two spellings of one install a few lines apart — exactly the confusion this PR exists to remove. find_installs now reports the PATH entry and canonicalizes only as a dedup key, so two entries reaching one file are still one install.
  • No test, deliberately. Linux resolves current_exe through /proc/self/exe before the CLI sees it, so a retargeted symlink still reads as a new install there and nothing in-process can recover the invoked path once the kernel has resolved it. The fix reaches only platforms that hand over the invoked path, which is where the suite cannot observe it. Recorded on the function and in the commit body rather than left implicit.

The cache-writer read races the background check spawned in cli.rs349c5e2

Real, and there was more to it than the race: doctor already runs its own unconditional check, so the background one made a single process fetch crates.io twice and write the one shared cache file concurrently — and in a terminal with a stale cache it printed the upgrade warning twice, once from each. Skipping the background check for doctor closes the race and removes the duplication; passing the previous writer in ahead of the spawn, the other option offered, would have closed only the race.

run_version's unbounded probe is unchanged and still tracked in #2676, as agreed in the earlier thread.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (1)

cmd/soroban-cli/src/commands/doctor.rs:208

  • Comparing the exact executable paths misclassifies the two binary names from one normal installation. If /usr/local/bin/soroban last wrote the shared cache and the user runs /usr/local/bin/stellar doctor, this branch warns about a “different Stellar CLI” even when both binaries came from the same install and version; check_installs explicitly recognizes that one install normally ships both names. Normalize known stellar/soroban sibling paths to a shared installation identity (while retaining the executable/version for diagnostics), and cover the cross-name same-install case.
    match (&writer.executable, &this_cli.executable) {
        (Some(written_by), Some(running)) if written_by == running => {

@Dione-b

Dione-b commented Aug 8, 2026

Copy link
Copy Markdown
Author

Ran an adversarial review pass on this branch and pushed a follow-up commit fixing three gaps it surfaced (a6af472):

  1. doctor had no timeout on its PATH probes. run_version shelled out to any executable answering to stellar/soroban with no bound — a stalled or misbehaving one could hang doctor indefinitely, the one command that exists to diagnose a broken install. It now runs through tokio::process::Command under a 2s timeout (find_installs/installed_version/check_installs became async as a result).

  2. UPGRADE_CHECK_GRACE (2s) was shorter than FETCH_TIMEOUT (5s). A fetch that took 3s — well inside its own timeout — could still lose the race: the grace period gave up first, the process exited, and the fetched versions were never written to the cache, silently reproducing the bug this PR fixes. UPGRADE_CHECK_GRACE is now FETCH_TIMEOUT + 1s, so it can't expire before a fetch its own timeout allowed to succeed.

  3. A panicked background upgrade-check task was silently swallowed. tokio::time::timeout(..).await.is_err() only catches the outer Elapsed — a task that panicked but was still observed within the grace period surfaced as Ok(Err(JoinError)), which the check treated as success with no log at all. It now matches all three outcomes and logs the panic case too.

All existing + new tests pass (10 unit + 14 doctor integration tests), plus clippy -D warnings and cargo fmt --check are clean.

Two lower-severity items from the review were left as follow-ups rather than blocking this PR:

  • parse_version_banner only scans the first line of a --version banner for a semver token — fine for every banner format observed so far, but worth broadening if a CLI ever prints something ahead of the version line.
  • The new doctor integration tests make live requests to crates.io (has_available_upgrade doesn't check STELLAR_NO_UPDATE_CHECK), so they depend on network access in CI.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (2)

cmd/soroban-cli/src/cli.rs:133

  • The PR description promises a 2-second grace period and calls out at most 2 seconds of added latency, but this computes 6 seconds (FETCH_TIMEOUT is 5 seconds). A fast first command can therefore block three times longer than the documented behavior. Either retain the stated 2-second cap or update the intended contract and PR description to the longer delay.
const UPGRADE_CHECK_GRACE: std::time::Duration =
    FETCH_TIMEOUT.saturating_add(std::time::Duration::from_secs(1));

cmd/soroban-cli/src/commands/doctor.rs:477

  • The timeout drops the Command::output() future, but Tokio processes default to kill_on_drop(false), so a timed-out probe keeps running and is not reaped. Repeated doctor runs can therefore accumulate stalled stellar/soroban processes. Configure the child to be killed when the timed-out future is dropped (or explicitly spawn, kill, and wait for it).
    let output = tokio::time::timeout(
        INSTALL_PROBE_TIMEOUT,
        tokio::process::Command::new(path).args(args).output(),
    )

The warning names a version but not the install it came from, and on a machine
with more than one Stellar CLI those are different questions. A stale `soroban`
left in `~/.cargo/bin` by an old `cargo install`, or a Homebrew install shadowed
by a newer one, prints "a new release is available: 22.1.0 -> 23.3.0" using its
own version -- and the user compares it against `stellar --version`, which
answers 25.2.0. The numbers disagree because two binaries are talking, which the
message gives no way to see. It now names the executable it is about.

The reported latest version could also be stale on its own. The check runs in a
background task that `main` drops on return, so a command finishing faster than
the request to crates.io left the fetched versions unwritten and the next run
starting over -- re-fetching each time while continuing to report whatever the
cache already held. The task now gets a grace period to land its result.

That grace has to outlast the fetch it waits on, so the fetch needed a bound of
its own: the shared HTTP client only limits how long connecting may take, so a
server that accepts and then stalls could hang the request indefinitely, and no
grace period can be chosen against a wait with no ceiling. With the fetch capped
at 5s the grace is that plus a second, which cannot expire before a fetch its
own timeout allowed to succeed.

The cost is on the first command of the day, and only when the check actually
goes to the network: up to 6s, where the previous behaviour was to drop the
check. `STELLAR_NO_UPDATE_CHECK` still turns it off entirely.

This does not reach warnings printed by already-released binaries -- an old
`soroban` will keep printing its old unannotated message, since the fix has to
be in the binary doing the printing. What it fixes is every warning from this
release forward.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
finish_upgrade_check awaited the background task's grace period after
root.run(), but every branch of the error handling above it exits via
std::process::exit, which ends the process without running anything
placed after it. Any command that returned an error -- a bad flag, a
failed simulation, an unreachable RPC -- skipped the grace period
entirely and killed the check before it could write its result to the
cache, reproducing the exact bug this was meant to fix.

Capturing root.run()'s result first and finishing the check before
acting on it means every exit path waits on it once, not only the
success path.

Co-authored-by: Nearx-Labs <nearxlabs@nearx.com.br>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog (Not Ready)

Development

Successfully merging this pull request may close these issues.

Upgrade check warning shows wrong latest version

2 participants